리다이렉션 없이 url 변경하기 해결

이번에는 프로젝트하면서 제가 어떻게 리다이렉션 없이 url을 변경했고, 그에따라 모달을 띄워주었는지 소개해보고자 합니다.

해당 문제를 해결 할 때 인스타그램의 웹을 많이 참고했습니다.

문제 파악하기

일단, 제가 원하는 /search 페이지의 구조는 아래와 같습니다.

  1. 사용자가 작성한 리뷰를 최초에 10개 가져온다.
  2. 인피니트 스크롤링을 통해 사용자가 스크롤을 내리면, 리뷰를 더 가져오도록 한다.
  3. 특정 리뷰를 클릭하면, 해당 리뷰를 볼 수 있는 모달 창이 띄워진다.
  4. 모달창을 띄우면 /search/[postId] 로 url 이 바뀐다. ( 모달 형태로 뒷 배경에 리뷰리스트가 보인다)
  5. /search/[postId] 를 url 로 접속 시, 개별 리뷰를 보여준다.

여기서 충돌되는 점은 인피니트 스크롤링url의 변경이었습니다.

모달창을 띄워주기 위해 url 을 변경한다면 ?

이 경우에는 기존에 로딩 되었던 데이터를 유지하는 과정이 추가됩니다. 물론 데이터를 캐싱해놓았고, 스크롤 위치도 저장되기 때문에 해당 과정이 복잡하지는 않지만, 저는 리뷰 리스트에서는 정말 리뷰 데이터가 ‘모달’ 처럼 띄워지는 것을 계획했기 때문에, 새로 페이지를 만들어서 뒷배경(overlay)에 리뷰 리스트를 다시 로딩하여 띄워주는 것은 매우 비효율적이라고 판단했습니다.


URL 변경 없이 간단히 모달창만 띄워준다면 ?

이 경우는 유저가 특정 리뷰 페이지의 url을 저장해놓고 나중에 다시 접근할 가능성도 있는데 인피니트 스크롤링 데이터가 사라진 후에 접속하게 되면 해당 리뷰 데이터가 제대로 로딩 되지 않게 됩니다. 즉, 개별 리뷰 페이지의 주소가 부재하게 됩니다.

즉, 사용자가 새로고침을 누르면, 모달창이 닫히고, 개별 리뷰로서의 페이지는 사라지기 때문에 저의 요구사항과는 맞지 않았습니다.


그러면 총 두가지의 문제를 해결해야합니다.

  1. 개별 리뷰 클릭 시, 모달창 오픈 + url 변경을 리다이렉션 없이 해준다.
  2. url 로 개별 리뷰 페이지 (/search/[postId]) 에 접근 했을 때, 리뷰 데이터를 제대로 전달해줘야한다.

문제 해결 하기

인스타그램 참고

인스타그램이 정확히 어떻게 구현되어있는지는 모르겠지만, 인스타그램을 웹으로 접속하여 개별 포스트를 클릭하면 저의 요구사항과 거의 같게 동작됩니다.

  1. 모달 형태로 띄워진다 (url 변경, 리다이렉션 X)
  2. 모달을 닫으면 이전 페이지로 돌아간다. (url 변경, 리다이렉션 X)
  3. 모달을 연 채로, 새로고침 하면 해당 포스트의 개별 페이지로 리다이렉션 된다.

저는 아래와 같은 이유로 인스타그램 역시, 모달 창을 띄웠을 때 리다이렉션 없이 url을 변경하지 않는 방식으로 구현되었다고 판단했습니다

  1. 모달형태로 띄워질 때 뒤의 overlay부분을 자세히보면 기존 포스트 리스트가 배경에 보이고 , 스크롤도 가능합니다. (overlay를 검은색으로 한 것은 스크롤을 하지 않도록 유도한 것 같다고 판단됩니다)
  2. 모달형태로 띄워진 후 브라우저의 뒤로가기 버튼을 누르면 제대로 동작되지 않습니다. (아래에서 리다이렉션 없이 url을 변경하는 방법을 사용 할 시, 뒤로가기 버튼을 누르면 모달이 닫히기 전 페이지로 가지 않습니다.)

그렇다면 인스타그램처럼 리다이렉션 없이 url을 변경하는 방법을 구현해보겠습니다.

즉, 모달창 오픈과 함께 url을 변경해주어야 하고, 이 때 리다이렉션을 일으키지 않아야합니다.

따라서 기존의 Router는 사용하지 못합니다.


리다이렉션 없이 url 변경하기

  • 먼저 리다이렉션 없이 url을 변경하는 것은 window.history.replaceState 메서드를 통해 가능합니다.
  • 첫번째 파라미터 obj 는 메서드에 전달될 history 항목과 연결될 객체를 넣을 수 있습니다. null 값도 가능합니다.
  • 두번째 파라미터 title은 현재 대부분의 브라우저에서 무시하는 파라미터이지만, 추후에 사용될 가능성을 위해 빈 문자열로 남겨둡니다. State의 타이틀을 삽입해도 됩니다.
  • 마지막 파라미터 url을 통해서 url을 바꿀 수 있습니다. 현재 url과 같은 origin 을 가지고 있어야 합니다.
window.history.replaceState(null, '', `/search/${postId}`) // 개별 리뷰 url로 가기
window.history.replaceState(null, '', '/search') // 기존 url로 돌아가기

적용하기

이를 모달 오픈과 클로즈에 적용하면 아래와 같이 구현됩니다.

const openModal = useCallback(
  (id: string) => async () => {
    window.history.replaceState(null, '', `${routes.POST}/${id}`)
    modalHandler()
  },
  [fullReviews, showModal]
)

const closeModal = useCallback(() => {
  window.history.replaceState(null, '', originPath)
  modalHandler()
}, [originPath, showModal])

두번째 문제 해결은 간단합니다. /post/[id] 페이지를 생성하고, 해당 페이지에서는 id 파라미터 값에 따라서 리뷰 데이터를 서버 혹은 캐시로부터 가져오면 됩니다.


구현 결과


이렇게 원하는 요구사항을 구현할 수 있었습니다.


This is@moonee
프론트엔드 개발 공부 블로그

GitHub